package com.gitee.qdbp.base.resolver;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.result.ResponseMessage;
import com.gitee.qdbp.able.result.ResultCode;
import com.gitee.qdbp.base.i18n.IResultCodeI18nResolver;

/**
 * 异常处理(支持@ResponseBody返回JSON)
 *
 * <pre>
 * &lt;bean id="handlerExceptionResolver" class="{pkg}.MappingExceptionSupportJsonResolver"&gt;
 *     &lt;property name="responseBodyView"&gt;
 *         &lt;bean class="com.alibaba.fastjson.support.spring.FastJsonJsonView" /&gt;
 *     &lt;/property&gt;
 *     &lt;property name="mappingExceptionResolver"&gt;
 *         &lt;bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"&gt;
 *             &lt;property name="defaultErrorView" value="error/500"/&gt;
 *         &lt;/bean&gt;
 *     &lt;/property&gt;
 * &lt;/bean&gt;
 * </pre>
 *
 * @author zhaohuihua
 * @version 150919
 */
public class JsonSupportMappingExceptionResolver extends AbstractHandlerMethodExceptionResolver {

    private Logger log = LoggerFactory.getLogger(JsonSupportMappingExceptionResolver.class);

    private SimpleMappingExceptionResolver mappingExceptionResolver;

    @Autowired(required = false)
    private IResultCodeI18nResolver resultCodeI18nResolver;

    private View responseBodyView;

    @Override
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
            HandlerMethod handler, Exception e) {

        boolean hasResponseBody = false;
        if (handler != null && handler.getMethod() != null) {
            Method method = handler.getMethod();
            ResponseBody rb = method.getAnnotation(ResponseBody.class);
            hasResponseBody = rb != null;
        }

        ResponseMessage rm;
        if (e instanceof ServiceException) {
            rm = new ResponseMessage((ServiceException) e);
        } else if (e.getCause() instanceof ServiceException) {
            // 提示具体的错误原因
            rm = new ResponseMessage((ServiceException) e.getCause());
        } else if (e instanceof AuthorizationException) {
            // 没有权限或认证失败
            rm = new ResponseMessage(ResultCode.FORBIDDEN);
        } else {
            log.error("Catched exception", e);
            // 未知异常
            rm = new ResponseMessage(ResultCode.SERVER_INNER_ERROR);
        }

        if (resultCodeI18nResolver != null) {
            // 根据当时语言替换返回码对应的错误提示
            String message = resultCodeI18nResolver.getResultCodeMessage(rm.getCode());
            if (message != null) {
                rm.setMessage(message);
            }
        }

        Map<String, Object> result = (JSONObject) JSON.toJSON(rm);
        if (hasResponseBody) {
            // 返回JSON格式
            return new ModelAndView(responseBodyView, result);
        } else {
            // 调用SimpleMappingExceptionResolver处理
            ModelAndView mv = mappingExceptionResolver.resolveException(request, response, handler, e);

            if (mv != null) {
                for (Entry<String, Object> entry : result.entrySet()) {
                    mv.addObject(entry.getKey(), entry.getValue());
                }
            }
            return mv;
        }
    }

    public void setResponseBodyView(View view) {
        this.responseBodyView = view;
    }

    public void setMappingExceptionResolver(SimpleMappingExceptionResolver mappingExceptionResolver) {
        this.mappingExceptionResolver = mappingExceptionResolver;
    }

    /** 结果返回码的国际化处理类 **/
    public void setResultCodeI18nResolver(IResultCodeI18nResolver resultCodeI18nResolver) {
        this.resultCodeI18nResolver = resultCodeI18nResolver;
    }
}
